/*     */ package com.zimbra.cs.pop3;
/*     */ 
/*     */ import com.zimbra.common.account.Key.AccountBy;
/*     */ import com.zimbra.common.localconfig.KnownKey;
/*     */ import com.zimbra.common.localconfig.LC;
/*     */ import com.zimbra.common.service.ServiceException;
/*     */ import com.zimbra.common.stats.StopWatch;
/*     */ import com.zimbra.common.util.ByteUtil;
/*     */ import com.zimbra.common.util.Log;
/*     */ import com.zimbra.common.util.ZimbraLog;
/*     */ import com.zimbra.cs.account.Account;
/*     */ import com.zimbra.cs.account.Provisioning;
/*     */ import com.zimbra.cs.account.auth.AuthContext.Protocol;
/*     */ import com.zimbra.cs.mailbox.MailboxManager;
/*     */ import com.zimbra.cs.mailbox.Message;
/*     */ import com.zimbra.cs.security.sasl.Authenticator;
/*     */ import com.zimbra.cs.security.sasl.AuthenticatorUser;
/*     */ import com.zimbra.cs.security.sasl.PlainAuthenticator;
/*     */ import com.zimbra.cs.server.ServerThrottle;
/*     */ import com.zimbra.cs.stats.ActivityTracker;
/*     */ import com.zimbra.cs.stats.ZimbraPerf;
/*     */ import java.io.IOException;
/*     */ import java.io.InputStream;
/*     */ import java.io.OutputStream;
/*     */ import java.io.PushbackInputStream;
/*     */ import java.net.InetAddress;
/*     */ import java.net.InetSocketAddress;
/*     */ import org.apache.commons.codec.binary.Base64;
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ abstract class Pop3Handler
/*     */ {
/*     */   static final int MIN_EXPIRE_DAYS = 31;
/*     */   static final int MAX_RESPONSE = 512;
/*     */   static final int MAX_RESPONSE_TEXT = 505;
/*  56 */   static final byte[] LINE_SEPARATOR = { 13, 10 };
/*     */   private static final String TERMINATOR = ".";
/*     */   private static final int TERMINATOR_C = 46;
/*  59 */   private static final byte[] TERMINATOR_BYTE = { 46 };
/*     */   
/*     */   final Pop3Config config;
/*     */   
/*     */   OutputStream output;
/*     */   
/*     */   boolean startedTLS;
/*     */   private String user;
/*     */   private String query;
/*     */   private String accountId;
/*     */   private String accountName;
/*     */   private Pop3Mailbox mailbox;
/*     */   private String command;
/*     */   private long startTime;
/*     */   int state;
/*  74 */   private int errorCount = 0;
/*     */   
/*     */   private boolean dropConnection;
/*     */   
/*     */   Authenticator authenticator;
/*     */   
/*     */   private String clientAddress;
/*     */   
/*     */   private String origRemoteAddress;
/*     */   static final int STATE_AUTHORIZATION = 1;
/*     */   static final int STATE_TRANSACTION = 2;
/*     */   static final int STATE_UPDATE = 3;
/*     */   private String currentCommandLine;
/*     */   private int expire;
/*     */   private final ServerThrottle throttle;
/*     */   
/*     */   Pop3Handler(Pop3Config config)
/*     */   {
/*  92 */     this.config = config;
/*  93 */     this.startedTLS = config.isSslEnabled();
/*  94 */     this.throttle = ServerThrottle.getThrottle(config.getProtocol()); }
/*     */   
/*     */   abstract void startTLS() throws IOException;
/*     */   
/*     */   abstract void completeAuthentication() throws IOException;
/*     */   
/*     */   abstract InetSocketAddress getLocalAddress();
/*     */   
/* 102 */   String getOrigRemoteIpAddr() { return this.origRemoteAddress; }
/*     */   
/*     */   void setOrigRemoteIpAddr(String ip)
/*     */   {
/* 106 */     this.origRemoteAddress = ip;
/*     */   }
/*     */   
/*     */   boolean startConnection(InetAddress remoteAddr) throws IOException
/*     */   {
/* 111 */     ZimbraLog.clearContext();
/* 112 */     this.clientAddress = remoteAddr.getHostAddress();
/* 113 */     ZimbraLog.addIpToContext(this.clientAddress);
/*     */     
/* 115 */     ZimbraLog.pop.info("connected");
/* 116 */     if (!this.config.isServiceEnabled()) {
/* 117 */       return false;
/*     */     }
/* 119 */     sendOK(this.config.getGreeting());
/* 120 */     this.state = 1;
/* 121 */     this.dropConnection = false;
/* 122 */     return true;
/*     */   }
/*     */   
/*     */   public void setLoggingContext() {
/* 126 */     ZimbraLog.clearContext();
/* 127 */     ZimbraLog.addAccountNameToContext(this.accountName);
/* 128 */     ZimbraLog.addIpToContext(this.clientAddress);
/* 129 */     ZimbraLog.addOrigIpToContext(this.origRemoteAddress);
/*     */   }
/*     */   
/*     */   boolean processCommand(String line) throws IOException
/*     */   {
/* 134 */     if ((line != null) && (this.authenticator != null) && (!this.authenticator.isComplete())) {
/* 135 */       return continueAuthentication(line);
/*     */     }
/*     */     
/* 138 */     this.command = null;
/* 139 */     this.startTime = 0L;
/* 140 */     this.currentCommandLine = line;
/*     */     try
/*     */     {
/* 143 */       boolean result = processCommandInternal();
/*     */       
/*     */ 
/* 146 */       if (this.startTime > 0L) {
/* 147 */         long elapsed = ZimbraPerf.STOPWATCH_POP.stop(this.startTime);
/* 148 */         if (this.command != null) {
/* 149 */           ZimbraPerf.POP_TRACKER.addStat(this.command.toUpperCase(), this.startTime);
/* 150 */           ZimbraLog.pop.info("%s elapsed=%d", new Object[] { this.command.toUpperCase(), Long.valueOf(elapsed) });
/*     */         } else {
/* 152 */           ZimbraLog.pop.info("(unknown) elapsed=%d", new Object[] { Long.valueOf(elapsed) });
/*     */         }
/*     */       }
/* 155 */       this.errorCount = 0;
/* 156 */       return result;
/*     */     } catch (Pop3CmdException e) {
/* 158 */       ZimbraLog.pop.debug(e.getMessage(), e);
/* 159 */       this.errorCount += 1;
/* 160 */       int errorLimit = LC.pop3_max_consecutive_error.intValue();
/* 161 */       if ((errorLimit > 0) && (this.errorCount >= errorLimit)) {
/* 162 */         ZimbraLog.pop.warn("dropping connection due to too many errors");
/* 163 */         sendERR(e.getResponse() + " : Dropping connection due to too many bad commands");
/* 164 */         this.dropConnection = true;
/*     */       } else {
/* 166 */         sendERR(e.getResponse());
/*     */       }
/* 168 */       return !this.dropConnection;
/*     */     } catch (ServiceException e) {
/* 170 */       sendERR(Pop3CmdException.getResponse(e.getMessage()));
/* 171 */       if ("mail.NO_SUCH_BLOB".equals(e.getCode())) {
/* 172 */         ZimbraLog.pop.warn(e.getMessage(), e);
/*     */       } else {
/* 174 */         ZimbraLog.pop.debug(e.getMessage(), e);
/*     */       }
/* 176 */       if (!this.dropConnection) tmpTernaryOp = true; } return false;
/*     */   }
/*     */   
/*     */   boolean processCommandInternal() throws Pop3CmdException, IOException, ServiceException
/*     */   {
/* 181 */     this.startTime = System.currentTimeMillis();
/* 182 */     this.command = this.currentCommandLine;
/* 183 */     String arg = null;
/*     */     
/* 185 */     if (this.command == null) {
/* 186 */       this.dropConnection = true;
/* 187 */       ZimbraLog.pop.info("disconnected without quit");
/* 188 */       return false;
/*     */     }
/*     */     
/* 191 */     if (ZimbraLog.pop.isTraceEnabled()) {
/* 192 */       if ("PASS ".regionMatches(true, 0, this.command, 0, 5)) {
/* 193 */         ZimbraLog.pop.trace("C: PASS ****");
/*     */       } else {
/* 195 */         ZimbraLog.pop.trace("C: %s", new Object[] { this.command });
/*     */       }
/*     */     }
/*     */     
/* 199 */     if (!this.config.isServiceEnabled()) {
/* 200 */       this.dropConnection = true;
/* 201 */       sendERR("Temporarily unavailable");
/* 202 */       return false;
/*     */     }
/*     */     
/* 205 */     int space = this.command.indexOf(" ");
/* 206 */     if (space > 0) {
/* 207 */       arg = this.command.substring(space + 1);
/* 208 */       this.command = this.command.substring(0, space);
/*     */     }
/*     */     
/* 211 */     if (this.command.length() < 1) {
/* 212 */       throw new Pop3CmdException("invalid request. please specify a command");
/*     */     }
/*     */     
/* 215 */     if (this.accountId != null) {
/*     */       try {
/* 217 */         Provisioning prov = Provisioning.getInstance();
/* 218 */         Account acct = prov.get(Key.AccountBy.id, this.accountId);
/* 219 */         if ((acct == null) || (!acct.getAccountStatus(prov).equals("active")))
/* 220 */           return false;
/*     */       } catch (ServiceException e) {
/* 222 */         ZimbraLog.pop.warn("ServiceException checking account status", e);
/* 223 */         return false;
/*     */       }
/* 225 */       if (this.throttle.isAccountThrottled(this.accountId, new String[] { this.origRemoteAddress, this.clientAddress })) {
/* 226 */         ZimbraLog.pop.warn("throttling POP3 connection for account %s due to too many requests", new Object[] { this.accountId });
/* 227 */         this.dropConnection = true;
/* 228 */         return false;
/*     */       }
/*     */     }
/*     */     
/* 232 */     if (this.throttle.isIpThrottled(this.origRemoteAddress)) {
/* 233 */       ZimbraLog.pop.warn("throttling POP3 connection for original remote IP %s", new Object[] { this.origRemoteAddress });
/* 234 */       this.dropConnection = true;
/* 235 */       return false; }
/* 236 */     if (this.throttle.isIpThrottled(this.clientAddress)) {
/* 237 */       ZimbraLog.pop.warn("throttling POP3 connection for remote IP %s", new Object[] { this.clientAddress });
/* 238 */       this.dropConnection = true;
/* 239 */       return false;
/*     */     }
/*     */     
/* 242 */     int ch = this.command.charAt(0);
/*     */     
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/* 248 */     switch (ch) {
/*     */     case 65: 
/*     */     case 97: 
/* 251 */       if ("AUTH".equalsIgnoreCase(this.command)) {
/* 252 */         doAUTH(arg);
/* 253 */         return true;
/*     */       }
/*     */       break;
/*     */     case 67: 
/*     */     case 99: 
/* 258 */       if ("CAPA".equalsIgnoreCase(this.command)) {
/* 259 */         doCAPA();
/* 260 */         return true;
/*     */       }
/*     */       break;
/*     */     case 68: 
/*     */     case 100: 
/* 265 */       if ("DELE".equalsIgnoreCase(this.command)) {
/* 266 */         doDELE(arg);
/* 267 */         return true;
/*     */       }
/*     */       break;
/*     */     case 76: 
/*     */     case 108: 
/* 272 */       if ("LIST".equalsIgnoreCase(this.command)) {
/* 273 */         doLIST(arg);
/* 274 */         return true;
/*     */       }
/*     */       break;
/*     */     case 78: 
/*     */     case 110: 
/* 279 */       if ("NOOP".equalsIgnoreCase(this.command)) {
/* 280 */         doNOOP();
/* 281 */         return true;
/*     */       }
/*     */       break;
/*     */     case 80: 
/*     */     case 112: 
/* 286 */       if ("PASS".equalsIgnoreCase(this.command)) {
/* 287 */         doPASS(arg);
/* 288 */         return true;
/*     */       }
/*     */       break;
/*     */     case 81: 
/*     */     case 113: 
/* 293 */       if ("QUIT".equalsIgnoreCase(this.command)) {
/* 294 */         doQUIT();
/* 295 */         return false;
/*     */       }
/*     */       break;
/*     */     case 82: 
/*     */     case 114: 
/* 300 */       if ("RETR".equalsIgnoreCase(this.command)) {
/* 301 */         doRETR(arg);
/* 302 */         return true; }
/* 303 */       if ("RSET".equalsIgnoreCase(this.command)) {
/* 304 */         doRSET();
/* 305 */         return true;
/*     */       }
/*     */       break;
/*     */     case 83: 
/*     */     case 115: 
/* 310 */       if ("STAT".equalsIgnoreCase(this.command)) {
/* 311 */         doSTAT();
/* 312 */         return true; }
/* 313 */       if ("STLS".equalsIgnoreCase(this.command)) {
/* 314 */         doSTLS();
/* 315 */         return true;
/*     */       }
/*     */       break;
/*     */     case 84: 
/*     */     case 116: 
/* 320 */       if ("TOP".equalsIgnoreCase(this.command)) {
/* 321 */         doTOP(arg);
/* 322 */         return true;
/*     */       }
/*     */       break;
/*     */     case 85: 
/*     */     case 117: 
/* 327 */       if ("UIDL".equalsIgnoreCase(this.command)) {
/* 328 */         doUIDL(arg);
/* 329 */         return true; }
/* 330 */       if ("USER".equalsIgnoreCase(this.command)) {
/* 331 */         doUSER(arg);
/* 332 */         return true;
/*     */       }
/*     */       break;
/*     */     case 88: 
/*     */     case 120: 
/* 337 */       if ("XOIP".equalsIgnoreCase(this.command)) {
/* 338 */         doXOIP(arg);
/* 339 */         return true;
/*     */       }
/*     */       
/*     */       break;
/*     */     }
/*     */     
/* 345 */     throw new Pop3CmdException("unknown command");
/*     */   }
/*     */   
/*     */   void sendERR(String response) throws IOException {
/* 349 */     sendResponse("-ERR", response, true);
/*     */   }
/*     */   
/*     */   void sendOK(String response) throws IOException {
/* 353 */     sendResponse("+OK", response, true);
/*     */   }
/*     */   
/*     */   private void sendOK(String response, boolean flush) throws IOException {
/* 357 */     sendResponse("+OK", response, flush);
/*     */   }
/*     */   
/*     */   void sendContinuation(String s) throws IOException {
/* 361 */     sendLine("+ " + s, true);
/*     */   }
/*     */   
/*     */   private void sendResponse(String status, String msg, boolean flush) throws IOException {
/* 365 */     sendLine(status + " " + msg, flush);
/*     */   }
/*     */   
/*     */ 
/*     */   abstract void sendLine(String paramString, boolean paramBoolean)
/*     */     throws IOException;
/*     */   
/*     */ 
/*     */   private void sendMessage(InputStream is, int maxNumBodyLines)
/*     */     throws IOException
/*     */   {
/* 376 */     boolean inBody = false;
/* 377 */     int numBodyLines = 0;
/*     */     
/* 379 */     PushbackInputStream stream = new PushbackInputStream(is);
/*     */     
/*     */ 
/* 382 */     boolean startOfLine = true;
/* 383 */     int lineLength = 0;
/*     */     int c;
/* 385 */     while ((c = stream.read()) != -1)
/* 386 */       if ((c == 13) || (c == 10)) {
/* 387 */         if (c == 13) {
/* 388 */           int peek = stream.read();
/* 389 */           if ((peek != 10) && (peek != -1)) {
/* 390 */             stream.unread(peek);
/*     */           }
/*     */         }
/* 393 */         if (!inBody) {
/* 394 */           if (lineLength == 0)
/* 395 */             inBody = true;
/*     */         } else {
/* 397 */           numBodyLines++;
/*     */         }
/* 399 */         startOfLine = true;
/* 400 */         lineLength = 0;
/* 401 */         this.output.write(LINE_SEPARATOR);
/*     */         
/* 403 */         if ((inBody) && (numBodyLines >= maxNumBodyLines)) {
/*     */           break;
/*     */         }
/*     */       } else {
/* 407 */         if ((c == 46) && (startOfLine)) {
/* 408 */           this.output.write(c);
/*     */         }
/* 410 */         if (startOfLine)
/* 411 */           startOfLine = false;
/* 412 */         lineLength++;
/* 413 */         this.output.write(c);
/*     */       }
/* 415 */     if (lineLength != 0) {
/* 416 */       this.output.write(LINE_SEPARATOR);
/*     */     }
/* 418 */     this.output.write(TERMINATOR_BYTE);
/* 419 */     this.output.write(LINE_SEPARATOR);
/* 420 */     this.output.flush();
/*     */   }
/*     */   
/*     */   private void doQUIT() throws IOException, ServiceException, Pop3CmdException {
/* 424 */     this.dropConnection = true;
/* 425 */     if (this.mailbox != null) {
/* 426 */       this.state = 3;
/* 427 */       int count = this.mailbox.expungeDeletes();
/* 428 */       if (count > 0) {
/* 429 */         sendOK("deleted " + count + " message(s)");
/*     */       } else {
/* 431 */         sendOK(this.config.getGoodbye());
/*     */       }
/*     */     } else {
/* 434 */       sendOK(this.config.getGoodbye());
/*     */     }
/* 436 */     ZimbraLog.pop.info("quit from client");
/*     */   }
/*     */   
/*     */   private void doNOOP() throws IOException {
/* 440 */     sendOK("yawn");
/*     */   }
/*     */   
/*     */   private void doRSET() throws Pop3CmdException, IOException {
/* 444 */     if (this.state != 2) {
/* 445 */       throw new Pop3CmdException("this command is only valid after a login");
/*     */     }
/* 447 */     int numUndeleted = this.mailbox.undeleteMarked();
/* 448 */     sendOK(numUndeleted + " message(s) undeleted");
/*     */   }
/*     */   
/*     */   private void doUSER(String username) throws Pop3CmdException, IOException {
/* 452 */     checkIfLoginPermitted();
/*     */     
/* 454 */     if (this.state != 1) {
/* 455 */       throw new Pop3CmdException("this command is only valid in authorization state");
/*     */     }
/* 457 */     if ((username == null) || (username.trim().isEmpty())) {
/* 458 */       throw new Pop3CmdException("please specify a user");
/*     */     }
/* 460 */     if (username.length() > 1024) {
/* 461 */       throw new Pop3CmdException("username length too long");
/*     */     }
/* 463 */     if (username.endsWith("}")) {
/* 464 */       int p = username.indexOf('{');
/* 465 */       if (p != -1) {
/* 466 */         this.user = username.substring(0, p);
/* 467 */         this.query = username.substring(p + 1, username.length() - 1);
/*     */       } else {
/* 469 */         this.user = username;
/*     */       }
/*     */     } else {
/* 472 */       this.user = username;
/*     */     }
/*     */     
/* 475 */     sendOK("hello " + this.user + ", please enter your password");
/*     */   }
/*     */   
/*     */   private void doPASS(String password) throws Pop3CmdException, IOException {
/* 479 */     checkIfLoginPermitted();
/*     */     
/* 481 */     if (this.state != 1) {
/* 482 */       throw new Pop3CmdException("this command is only valid in authorization state");
/*     */     }
/* 484 */     if (this.user == null) {
/* 485 */       throw new Pop3CmdException("please specify username first with the USER command");
/*     */     }
/* 487 */     if (password == null) {
/* 488 */       throw new Pop3CmdException("please specify a password");
/*     */     }
/* 490 */     if (password.length() > 1024) {
/* 491 */       throw new Pop3CmdException("password length too long");
/*     */     }
/* 493 */     authenticate(this.user, null, password, null);
/* 494 */     sendOK("server ready");
/*     */   }
/*     */   
/*     */   private void doAUTH(String arg) throws IOException {
/* 498 */     if (isAuthenticated()) {
/* 499 */       sendERR("command only valid in AUTHORIZATION state");
/* 500 */       return;
/*     */     }
/* 502 */     if ((arg == null) || (arg.length() == 0)) {
/* 503 */       sendERR("mechanism not specified");
/* 504 */       return;
/*     */     }
/*     */     
/* 507 */     int i = arg.indexOf(' ');
/* 508 */     String mechanism = i > 0 ? arg.substring(0, i) : arg;
/* 509 */     String initialResponse = i > 0 ? arg.substring(i + 1) : null;
/*     */     
/* 511 */     AuthenticatorUser authUser = new Pop3AuthenticatorUser(this);
/* 512 */     Authenticator auth = Authenticator.getAuthenticator(mechanism, authUser);
/*     */     
/* 514 */     if (auth == null) {
/* 515 */       sendERR("mechanism not supported");
/* 516 */       return;
/*     */     }
/*     */     
/* 519 */     this.authenticator = auth;
/* 520 */     this.authenticator.setLocalAddress(getLocalAddress().getAddress());
/* 521 */     if (!this.authenticator.initialize()) {
/* 522 */       this.authenticator = null;
/* 523 */       return;
/*     */     }
/*     */     
/* 526 */     if (initialResponse != null) {
/* 527 */       continueAuthentication(initialResponse);
/*     */     } else {
/* 529 */       sendContinuation("");
/*     */     }
/*     */   }
/*     */   
/*     */   private boolean continueAuthentication(String response) throws IOException {
/* 534 */     byte[] b = Base64.decodeBase64(response.getBytes("us-ascii"));
/* 535 */     this.authenticator.handle(b);
/* 536 */     if (this.authenticator.isComplete()) {
/* 537 */       if (this.authenticator.isAuthenticated()) {
/* 538 */         completeAuthentication();
/*     */       } else {
/* 540 */         this.authenticator = null;
/*     */       }
/*     */     }
/* 543 */     return true;
/*     */   }
/*     */   
/*     */   private boolean isAuthenticated() {
/* 547 */     return (this.state != 1) && (this.accountId != null);
/*     */   }
/*     */   
/*     */   void authenticate(String username, String authenticateId, String password, Authenticator auth) throws Pop3CmdException
/*     */   {
/* 552 */     String mechanism = auth != null ? auth.getMechanism() : "LOGIN";
/*     */     try
/*     */     {
/* 555 */       if (auth == null) {
/* 556 */         auth = new PlainAuthenticator(new Pop3AuthenticatorUser(this));
/* 557 */         authenticateId = username;
/*     */       }
/*     */       
/*     */ 
/* 561 */       Account acct = auth.authenticate(username, authenticateId, password, AuthContext.Protocol.pop3, getOrigRemoteIpAddr(), this.clientAddress, null);
/*     */       
/* 563 */       if (acct == null) {
/* 564 */         throw new Pop3CmdException("LOGIN failed");
/*     */       }
/* 566 */       if (!acct.getBooleanAttr("zimbraPop3Enabled", false)) {
/* 567 */         throw new Pop3CmdException("pop access not enabled for account");
/*     */       }
/* 569 */       this.accountId = acct.getId();
/* 570 */       this.accountName = acct.getName();
/*     */       
/* 572 */       ZimbraLog.addAccountNameToContext(this.accountName);
/* 573 */       ZimbraLog.pop.info("user %s authenticated, mechanism=%s %s", new Object[] { this.accountName, mechanism, this.startedTLS ? "[TLS]" : "" });
/*     */       
/*     */ 
/* 576 */       this.mailbox = new Pop3Mailbox(MailboxManager.getInstance().getMailboxByAccount(acct), acct, this.query);
/* 577 */       this.state = 2;
/* 578 */       this.expire = ((int)(acct.getTimeInterval("zimbraMailMessageLifetime", 0L) / 86400000L));
/* 579 */       if ((this.expire > 0) && (this.expire < 31)) {
/* 580 */         this.expire = 31;
/*     */       }
/*     */     } catch (ServiceException e) {
/* 583 */       String code = e.getCode();
/* 584 */       if ((code.equals("account.NO_SUCH_ACCOUNT")) || (code.equals("account.AUTH_FAILED")))
/* 585 */         throw new Pop3CmdException("LOGIN failed");
/* 586 */       if (code.equals("account.CHANGE_PASSWORD"))
/* 587 */         throw new Pop3CmdException("your password has expired");
/* 588 */       if (code.equals("account.MAINTENANCE_MODE")) {
/* 589 */         throw new Pop3CmdException("your account is having maintenance peformed. please try again later");
/*     */       }
/* 591 */       throw new Pop3CmdException(e.getMessage());
/*     */     }
/*     */   }
/*     */   
/*     */   private void checkIfLoginPermitted() throws Pop3CmdException
/*     */   {
/* 597 */     if ((!this.startedTLS) && (!this.config.isCleartextLoginsEnabled())) {
/* 598 */       throw new Pop3CmdException("only valid after entering TLS mode");
/*     */     }
/*     */   }
/*     */   
/*     */   boolean isSSLEnabled() {
/* 603 */     return this.startedTLS;
/*     */   }
/*     */   
/*     */   private void doSTAT() throws Pop3CmdException, IOException {
/* 607 */     if (this.state != 2) {
/* 608 */       throw new Pop3CmdException("this command is only valid after a login");
/*     */     }
/* 610 */     sendOK(this.mailbox.getNumMessages() + " " + this.mailbox.getSize());
/*     */   }
/*     */   
/*     */   private void doSTLS() throws Pop3CmdException, IOException {
/* 614 */     if (this.config.isSslEnabled()) {
/* 615 */       throw new Pop3CmdException("command not valid over SSL");
/*     */     }
/* 617 */     if (this.state != 1) {
/* 618 */       throw new Pop3CmdException("this command is only valid prior to login");
/*     */     }
/* 620 */     if (this.startedTLS) {
/* 621 */       throw new Pop3CmdException("command not valid while in TLS mode");
/*     */     }
/* 623 */     startTLS();
/* 624 */     this.startedTLS = true;
/*     */   }
/*     */   
/*     */   private void doLIST(String msg) throws Pop3CmdException, IOException {
/* 628 */     if (this.state != 2) {
/* 629 */       throw new Pop3CmdException("this command is only valid after a login");
/*     */     }
/* 631 */     if (msg != null) {
/* 632 */       Pop3Message pm = this.mailbox.getPop3Msg(msg);
/* 633 */       sendOK(msg + " " + pm.getSize());
/*     */     } else {
/* 635 */       sendOK(this.mailbox.getNumMessages() + " messages", false);
/* 636 */       int totNumMsgs = this.mailbox.getTotalNumMessages();
/* 637 */       for (int n = 0; n < totNumMsgs; n++) {
/* 638 */         Pop3Message pm = this.mailbox.getMsg(n);
/* 639 */         if (!pm.isDeleted()) {
/* 640 */           sendLine(n + 1 + " " + pm.getSize(), false);
/*     */         }
/*     */       }
/* 643 */       sendLine(".", true);
/*     */     }
/*     */   }
/*     */   
/*     */   private void doUIDL(String msg) throws Pop3CmdException, IOException {
/* 648 */     if (this.state != 2) {
/* 649 */       throw new Pop3CmdException("this command is only valid after a login");
/*     */     }
/* 651 */     if (msg != null) {
/* 652 */       Pop3Message pm = this.mailbox.getPop3Msg(msg);
/* 653 */       sendOK(msg + " " + pm.getId() + "." + pm.getDigest());
/*     */     } else {
/* 655 */       sendOK(this.mailbox.getNumMessages() + " messages", false);
/* 656 */       int totNumMsgs = this.mailbox.getTotalNumMessages();
/* 657 */       for (int n = 0; n < totNumMsgs; n++) {
/* 658 */         Pop3Message pm = this.mailbox.getMsg(n);
/* 659 */         if (!pm.isDeleted()) {
/* 660 */           sendLine(n + 1 + " " + pm.getId() + "." + pm.getDigest(), false);
/*     */         }
/*     */       }
/* 663 */       sendLine(".", true);
/*     */     }
/*     */   }
/*     */   
/*     */   private int parseInt(String s, String message) throws Pop3CmdException {
/*     */     try {
/* 669 */       return Integer.parseInt(s);
/*     */     } catch (NumberFormatException e) {
/* 671 */       throw new Pop3CmdException(message);
/*     */     }
/*     */   }
/*     */   
/*     */   private void doRETR(String msg) throws Pop3CmdException, IOException, ServiceException {
/* 676 */     if (this.state != 2) {
/* 677 */       throw new Pop3CmdException("this command is only valid after a login");
/*     */     }
/* 679 */     if (msg == null) {
/* 680 */       throw new Pop3CmdException("please specify a message");
/*     */     }
/* 682 */     Message m = this.mailbox.getMessage(msg);
/* 683 */     InputStream is = null;
/*     */     try {
/* 685 */       is = m.getContentStream();
/* 686 */       sendOK("message follows", false);
/* 687 */       sendMessage(is, Integer.MAX_VALUE);
/*     */     } finally {
/* 689 */       ByteUtil.closeStream(is);
/*     */     }
/* 691 */     this.mailbox.getPop3Msg(msg).setRetrieved(true);
/*     */   }
/*     */   
/*     */   private void doTOP(String arg) throws Pop3CmdException, IOException, ServiceException {
/* 695 */     if (this.state != 2) {
/* 696 */       throw new Pop3CmdException("this command is only valid after a login");
/*     */     }
/* 698 */     int space = arg == null ? -1 : arg.indexOf(" ");
/* 699 */     if (space == -1) {
/* 700 */       throw new Pop3CmdException("please specify a message and number of lines");
/*     */     }
/* 702 */     String msg = arg.substring(0, space);
/* 703 */     int n = parseInt(arg.substring(space + 1), "unable to parse number of lines");
/*     */     
/* 705 */     if (n < 0) {
/* 706 */       throw new Pop3CmdException("please specify a non-negative value for number of lines");
/*     */     }
/* 708 */     if ((msg == null) || (msg.equals(""))) {
/* 709 */       throw new Pop3CmdException("please specify a message");
/*     */     }
/* 711 */     Message m = this.mailbox.getMessage(msg);
/* 712 */     InputStream is = null;
/*     */     try {
/* 714 */       is = m.getContentStream();
/* 715 */       sendOK("message top follows", false);
/* 716 */       sendMessage(is, n);
/*     */     } finally {
/* 718 */       ByteUtil.closeStream(is);
/*     */     }
/*     */   }
/*     */   
/*     */   private void doDELE(String msg) throws Pop3CmdException, IOException {
/* 723 */     if (this.state != 2) {
/* 724 */       throw new Pop3CmdException("this command is only valid after a login");
/*     */     }
/* 726 */     if (msg == null) {
/* 727 */       throw new Pop3CmdException("please specify a message");
/*     */     }
/* 729 */     Pop3Message pm = this.mailbox.getPop3Msg(msg);
/* 730 */     this.mailbox.delete(pm);
/* 731 */     sendOK("message " + msg + " marked for deletion");
/*     */   }
/*     */   
/*     */   private void doCAPA() throws IOException
/*     */   {
/* 736 */     sendOK("Capability list follows", false);
/* 737 */     sendLine("TOP", false);
/* 738 */     sendLine("USER", false);
/* 739 */     sendLine("UIDL", false);
/* 740 */     if (!this.config.isSslEnabled()) {
/* 741 */       sendLine("STLS", false);
/*     */     }
/* 743 */     sendLine("SASL" + getSaslCapabilities(), false);
/* 744 */     if (this.state != 2) {
/* 745 */       sendLine("EXPIRE 31 USER", false);
/*     */     }
/* 747 */     else if (this.expire == 0) {
/* 748 */       sendLine("EXPIRE NEVER", false);
/*     */     } else {
/* 750 */       sendLine("EXPIRE " + this.expire, false);
/*     */     }
/*     */     
/* 753 */     sendLine("XOIP", false);
/*     */     
/* 755 */     sendLine("IMPLEMENTATION ZimbraInc", false);
/* 756 */     sendLine(".", true);
/*     */   }
/*     */   
/*     */   private void doXOIP(String origIp) throws Pop3CmdException, IOException {
/* 760 */     if (origIp == null) {
/* 761 */       throw new Pop3CmdException("please specify an ip address");
/*     */     }
/* 763 */     String curOrigRemoteIp = getOrigRemoteIpAddr();
/* 764 */     if (curOrigRemoteIp == null) {
/* 765 */       setOrigRemoteIpAddr(origIp);
/* 766 */       ZimbraLog.addOrigIpToContext(origIp);
/* 767 */       ZimbraLog.pop.info("POP3 client identified as: %s", new Object[] { origIp });
/*     */     }
/* 769 */     else if (curOrigRemoteIp.equals(origIp)) {
/* 770 */       ZimbraLog.pop.warn("POP3 XOIP is allowed only once per session, command ignored");
/*     */     } else {
/* 772 */       ZimbraLog.pop.error("POP3 XOIP is allowed only once per session, received different IP: %s, command ignored", new Object[] { origIp });
/*     */     }
/*     */     
/*     */ 
/*     */ 
/* 777 */     sendOK("");
/*     */   }
/*     */   
/*     */   private String getSaslCapabilities() {
/* 781 */     AuthenticatorUser authUser = new Pop3AuthenticatorUser(this);
/* 782 */     StringBuilder sb = new StringBuilder();
/* 783 */     for (String mechanism : Authenticator.listMechanisms()) {
/* 784 */       if (Authenticator.getAuthenticator(mechanism, authUser) != null)
/* 785 */         sb.append(' ').append(mechanism);
/*     */     }
/* 787 */     return sb.toString();
/*     */   }
/*     */ }


/* Location:              /home/mint/zimbrastore.jar!/com/zimbra/cs/pop3/Pop3Handler.class
 * Java compiler version: 7 (51.0)
 * JD-Core Version:       0.7.1
 */